package com.sn.author.service.impl;

import com.alibaba.fastjson.JSON;
import com.sn.author.common.enums.JobEnums;
import com.sn.author.common.enums.TaskEnums;
import com.sn.author.common.enums.TaskTypeEnum;
import com.sn.author.common.exception.BadRequestException;
import com.sn.author.dao.ImportAccountMapper;
import com.sn.author.dao.ImportVideoMapper;
import com.sn.author.dao.JobAccountMapper;
import com.sn.author.dao.TaskMapper;
import com.sn.author.domain.dto.TaskBasisInfo;
import com.sn.author.domain.entity.ImportAccount;
import com.sn.author.domain.entity.ImportVideo;
import com.sn.author.domain.entity.JobAccount;
import com.sn.author.domain.entity.Task;
import com.sn.author.domain.taskEntity.TaskModel;
import com.sn.author.domain.taskEntity.TaskProcess;
import com.sn.author.domain.vo.TaskVo;
import com.sn.author.domain.vo.LoginVo;
import com.sn.author.filter.content.SecurityContextHolder;
import com.sn.author.redis.RedisConst;
import com.sn.author.redis.RedisKeyBuild;
import com.sn.author.redis.RedisUtil;
import com.sn.author.service.dao.PushMessageService;
import com.sn.author.service.dao.TaskService;
import com.sn.author.utils.DateUtils;
import com.sn.author.utils.Utils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author xiaom
 * @Date 2020/9/21 19:08
 * @Version 1.0.0
 * @Description <>
 **/
@Service
@Slf4j
public class TaskServiceImpl implements TaskService {

    @Resource
    private JobAccountMapper jobAccountMapper;
    @Resource
    private ImportAccountMapper importAccountMapper;
    @Resource
    private ImportVideoMapper importVideoMapper;

    @Resource
    private TaskMapper taskMapper;

    @Autowired
    private PushMessageService pushMessageService;

    /**
     * 生成任务到redis队列
     * @param jobs    任务集合
     * @param taskId  任务ID
     */
    @Override
    public void generateJobToPool(List<TaskModel> jobs, Long taskId) {
        String taskType = TaskServiceImpl.parserKey(jobs.get(0).getTaskType());
        //添加任务到队列
        RedisUtil._REDIS_MODULE.push(RedisKeyBuild.buildTaskQueueKey(taskType),
                jobs.parallelStream().map(JSON::toJSONString).toArray(String[]::new));
        //记录任务基本信息
        RedisUtil._REDIS_MODULE.set(RedisKeyBuild.buildTaskProcessKey(taskType,taskId + ""),buildTaskProcessInfo(jobs.size(),taskId));
        log.info("开始生成任务 任务个数:{} taskType:{} taskId:{}",jobs.size(),taskType,taskId);
    }

    @Override
    public TaskProcess getProcessMessage(Long taskId,Integer taskType) {
        String key = RedisKeyBuild.buildTaskProcessKey(TaskServiceImpl.parserKey(taskType),taskId + "");
        String value = RedisUtil._REDIS_MODULE.get(key);
        if(value == null || "".equals(value)){
            return null;
        }
        return JSON.parseObject(value,TaskProcess.class);
    }

    @Override
    public void stopTask(String taskId, Integer taskType) {
        String taskKey = parserKey(taskType);
        Assert.notNull(taskKey,"不存在的taskType:" + taskType);

        RedisUtil._REDIS_MODULE.del(RedisKeyBuild.buildTaskQueueKey(taskKey));
        RedisUtil._REDIS_MODULE.del(RedisKeyBuild.buildTaskProcessKey(taskKey,taskId));

        log.info("停止任务成功 taskId:[{}] taskType:[{}]",taskId,taskKey);
    }

    public static String parserKey(Integer taskType){
        Optional<TaskTypeEnum> typeEnum = TaskTypeEnum.match(taskType);
        if(!typeEnum.isPresent()){
            throw new BadRequestException("异常的任务类型:" + taskType);
        }
        switch (typeEnum.get()){
            case LOGIN:
                return RedisConst._LOGIN;
            case ATTENTION:
                return RedisConst._ATTENTION;
            case LIKE:
                return RedisConst._LIKE;
            case COMMENT:
                return RedisConst._COMMENT;
            case VIDEOINFO:
                return RedisConst._VIDEOINFO;
            case IM:
                return RedisConst._IM;
            case COLLECTION:
                return RedisConst._COLLECTION;
            default:
                log.error("不可能存在的异常");
        }
        return null;
    }

    private TaskProcess buildTaskProcessInfo(int size, Long taskId) {
        TaskProcess taskProcess = new TaskProcess();
        taskProcess.setOverCnt(0);
        taskProcess.setTaskId(taskId + "");
        taskProcess.setTotalCnt(size);
        return taskProcess;
    }

    /**
     * 构建任务模型
     */
    public static List<TaskModel> buildAttentionTaskModelList(List<JobAccount> jobs,Integer taskType){
        return jobs.stream().map(job -> {
            TaskModel model = new TaskModel();
            model.setTaskId(job.getTaskId() + "");
            model.setTaskType(taskType);
            model.setTaskParam(job);
            return model;
        }).collect(Collectors.toList());
    }

    /**
     * 构建登录抖音任务模型
     * @param accounts
     * @return
     */
    public static List<TaskModel> buildLoginTaskModelList(List<LoginVo> accounts,Long taskId){
        return accounts.stream().map(account -> {
            TaskModel model = new TaskModel();
            model.setTaskType(TaskTypeEnum.LOGIN.getType());
            model.setTaskParam(account);
            model.setTaskId(taskId + "");
            return model;
        }).collect(Collectors.toList());

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public TaskBasisInfo start(TaskVo taskVo) {
        validIsAll(taskVo);
        //生成任务信息数据集
        Task task = buildTaskBasisInfo(taskVo);
        taskMapper.insert(task);
        //详细任务数据
        List<JobAccount> jobs = buildTaskDetailInfo(taskVo,task.getId());
        jobAccountMapper.batchInsert(jobs);
        //生成任务到任务池
        generateJobToPool(TaskServiceImpl.buildAttentionTaskModelList(jobs, taskVo.getType()),task.getId());
        try {
            pushMessageService.pushMessage(PushMessageService.buildTaskPushMessage(TaskService.getTaskMsg(taskVo.getType()),"-1",taskVo.getType()));
        } catch (Exception e) {
            log.error("推送webSocket Message失败 e:{}",e.getMessage());
        }
        return new TaskBasisInfo(task.getId(), taskVo.getType());
    }

    @Override
    public void updateTaskStatusByTaskId(String taskId, Integer status) {
        taskMapper.updateStatusByTaskId(taskId,status);
        jobAccountMapper.updateStatusByTaskId(taskId,status);
    }

    private void validIsAll(TaskVo taskVo) {
        if(taskVo.getAccounts().size() == 1 && taskVo.getAccounts().get(0).equals("-1")){
            //获取所有用户
            taskVo.setAccounts(importAccountMapper.selectAll().stream().map(ImportAccount::getPhone).collect(Collectors.toList()));
        }
        if(taskVo.getVideoUrl().size() == 1 && taskVo.getVideoUrl().get(0).equals("-1")){
            taskVo.setVideoUrl(importVideoMapper.selectAll().stream().map(ImportVideo::getVideoUrl).collect(Collectors.toList()));
        }
    }

    private Task buildTaskBasisInfo(TaskVo taskVo) {
        Task task = new Task();
        task.setAccountCount(taskVo.getAccounts().size());
        task.setTaskConfig(JSON.toJSONString(taskVo));
        task.setStartTime(DateUtils.formatYMDHMS(new Date()));
        task.setUserId(SecurityContextHolder.getCurrentUserId());
        task.setVideoCount(taskVo.getVideoUrl().size());
        task.setStatus(TaskEnums.AWAIT.getCode());
        task.setTaskId(Utils.getGuid());
        task.setTaskType(taskVo.getType());
        return task;
    }

    private List<JobAccount> buildTaskDetailInfo(TaskVo taskVo, Long taskId) {
        List<String> accountList = taskVo.getAccounts();

        if(TaskTypeEnum.IM.getType().equals(taskVo.getType())){
            //如果是IM私信 需要更改一下结构
            replaceStructure(taskVo);
        }

        List<String> videoUrlList = taskVo.getVideoUrl();

        List<JobAccount> result = new ArrayList<>();

        videoUrlList.forEach(url -> accountList.forEach(account -> {
            JobAccount job = new JobAccount();
            job.setAccount(account);
            job.setCreateTime(DateUtils.formatYMDHMS(new Date()));
            job.setSeqId(Utils.getGuid());
            job.setVideoUrl(url);
            job.setTaskId(taskId);
            job.setExecuteStatus(TaskEnums.AWAIT.getCode());
            job.setComment(taskVo.getContent());
            job.setCommentType(taskVo.getCommentType());
            job.setAutomaticComment(taskVo.getAutomaticComment());
            job.setExecuteParams(buildParams(taskVo));
            job.setTaskType(taskVo.getType());
            result.add(job);
        }));
        return result;
    }

    private String buildParams(TaskVo taskVo) {
        HashMap<String,String> map = new HashMap<>();
        map.put("url",taskVo.getUrl());
        map.put("title",taskVo.getTitle());
        map.put("desc",taskVo.getDesc());
        map.put("image",taskVo.getImage());
        map.put("collectionNum",taskVo.getCollectionNum() + "");

        return JSON.toJSONString(map);
    }

    private void replaceStructure(TaskVo taskVo) {

        StringBuilder ids = new StringBuilder();

        List<String> videos = taskVo.getVideoUrl();
        for (String id : videos) {
            ids.append(id).append(",");
        }

        taskVo.setVideoUrl(Collections.singletonList(ids.toString()));
    }


    @Override
    public Boolean stop(String taskId, Integer taskType) {
        Assert.isTrue(!"".equals(taskId),"taskId不能为空");
        stopTask(taskId,taskType);
        //推送消息
        pushMessageService.pushMessage(PushMessageService.buildTaskPushMessage("停止 <" + TaskService.getTaskDesc(taskType) + "> 任务成功",taskId,taskType));
        return true;
    }

}
